package grape.common.service.common;

import cn.hutool.core.util.ReflectUtil;
import com.baomidou.mybatisplus.core.conditions.AbstractWrapper;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.interfaces.Join;
import com.baomidou.mybatisplus.core.conditions.interfaces.Nested;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.IService;
import grape.common.service.common.dataconstraint.ConstraintCompiledContent;
import grape.common.service.po.IDBasePo;
import grape.common.service.query.BasePoQuery;
import grape.common.service.update.BaseUpdateWithNullCondition;
import grape.common.tools.StringTool;
import grape.common.tools.ToolService;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 所有service接口的父接口，以实现通用功能
 * Created by yangwei
 * Created at 2019/7/23 14:30
 */
public interface IBaseService<Po extends IDBasePo<?,?>> extends IService<Po>, ToolService {

    /**
     * 保存实体，操作同com.baomidou.mybatisplus.extension.service.IService#save(java.lang.Object)
     * 之所以加这个是兼容rpc的远程调用，controller调用service时建议使用该方法插入数据，以获取插入后的实体数据id
     *
     * @param entity
     * @return 返回插入后的实体
     */
    Po insert(Po entity);

    /**
     * 提取po们的id
     * @param pos
     * @return
     */
    default List<String> convertIds(List<Po> pos){
        if (!isEmpty(pos)) {
            return pos.stream().map((p) -> p.getId().toString()).collect(Collectors.toList());
        }
        return null;
    }

    /**
     * 有获取多条数据的风险，请确保参数条件只能查到一条数据
     * @param query
     * @return
     */
    default Po getOne(Po query){
        return getOne(Wrappers.query(query));
    }
    /**
     * 根据id查询，附加额外条件
     * @param ids
     * @param query
     * @return
     */
    default List<Po> listByIds(Collection<String> ids,Po query){
        if (isEmpty(ids)) {
            return null;
        }
        return list(Wrappers.query(query).in(IDBasePo.COLUMN_ID, ids));
    }
    /**
     * 分页查询的默认接口方法
     * @param page
     * @param query
     * @return
     */
    default IPage<Po> page(IPage<Po> page, Po query){
        return page(page, Wrappers.query(query));
    }

    /**
     * 分页查询接口，具有添加额外额外查询条件的能力
     * 子类实现该接口时，需要强转类型为需要的类型以获取额外的查询参数
     * 因为是默认实现，所以只考虑了实体参数，额外参数在基类中获取不到，或许加一些额外参数注解可以实现自动拼接条件
     * @param page
     * @param query
     * @return
     */
    default IPage<Po> page(IPage<Po> page, BasePoQuery<Po> query){
        Wrapper<Po> queryWrapper = query.getQueryWrapper();
        if (queryWrapper == null) {
            queryWrapper = Wrappers.query(query.getPoQuery());
        }else {
            if (!query.queryWrapperOnly()) {
                ((AbstractWrapper) queryWrapper).setEntity(query.getPoQuery());
            }
        }
        innerAnnotationSupportWrapper(queryWrapper, query);
        return page(page, queryWrapper);
    }
    /**
     * 分页查询的默认接口方法,带数据约束
     * @param page
     * @param query
     * @param constraintContents 能直接使用的约束sql
     * @return
     */
    default IPage<Po> page(IPage<Po> page, Po query,List<ConstraintCompiledContent> constraintContents){
        QueryWrapper<Po> queryWrapper = Wrappers.query(query);
        innerConstraintContentsWrapper(queryWrapper, constraintContents);

        return page(page, queryWrapper);
    }

    /**
     * 分页查询接口，,带数据约束,具有添加额外额外查询条件的能力
     * 子类实现该接口时，需要强转类型为需要的类型以获取额外的查询参数
     * 因为是默认实现，所以只考虑了实体参数，额外参数在基类中获取不到，或许加一些额外参数注解可以实现自动拼接条件
     * @param page
     * @param query
     * @param constraintContents
     * @return
     */
    default IPage<Po> page(IPage<Po> page, BasePoQuery<Po> query,List<ConstraintCompiledContent> constraintContents){
        Wrapper<Po> queryWrapper = query.getQueryWrapper();
        if (queryWrapper == null) {
            queryWrapper = Wrappers.query(query.getPoQuery());
        }else {
            if (!query.queryWrapperOnly()) {
                ((AbstractWrapper) queryWrapper).setEntity(query.getPoQuery());
            }
        }
        innerAnnotationSupportWrapper(queryWrapper, query);
        innerConstraintContentsWrapper(queryWrapper, constraintContents);

        return page(page, queryWrapper);
    }
    /**
     * 不分页查询的默认接口方法
     * @param query
     * @return
     */
    default List<Po> list(Po query){
        return list(Wrappers.query(query));
    }

    /**
     * 不分页查询接口，具有添加额外额外查询条件的能力
     * 子类实现该接口时，需要强转类型为需要的类型以获取额外的查询参数
     * 因为是默认实现，所以只考虑了实体参数，额外参数在基类中获取不到，或许加一些额外参数注解可以实现自动拼接条件
     * @param query
     * @return
     */
    default List<Po> list( BasePoQuery<Po> query){
        Wrapper<Po> queryWrapper = query.getQueryWrapper();
        if (queryWrapper == null) {
            queryWrapper = Wrappers.<Po>query(query.getPoQuery());
        }else {
            if (!query.queryWrapperOnly()) {
                ((AbstractWrapper) queryWrapper).setEntity(query.getPoQuery());
            }
        }
        innerAnnotationSupportWrapper(queryWrapper, query);
        return list(queryWrapper);
    }
    /**
     * 不分页查询的默认接口方法,带数据约束
     * @param query
     * @param constraintContents 能直接使用的约束sql
     * @return
     */
    default List<Po> list(Po query,List<ConstraintCompiledContent> constraintContents){

        QueryWrapper<Po> queryWrapper = Wrappers.query(query);
        innerConstraintContentsWrapper(queryWrapper, constraintContents);

        return list(queryWrapper);
    }

    /**
     * 不分页查询接口，,带数据约束,具有添加额外额外查询条件的能力
     * 子类实现该接口时，需要强转类型为需要的类型以获取额外的查询参数
     * 因为是默认实现，所以只考虑了实体参数，额外参数在基类中获取不到，或许加一些额外参数注解可以实现自动拼接条件
     * @param query
     * @param constraintContents
     * @return
     */
    default List<Po> list(BasePoQuery<Po> query,List<ConstraintCompiledContent> constraintContents){
        Wrapper<Po> queryWrapper = query.getQueryWrapper();

        if (queryWrapper == null) {
            queryWrapper = Wrappers.query(query.getPoQuery());
        }else {
            if (!query.queryWrapperOnly()) {
                ((AbstractWrapper) queryWrapper).setEntity(query.getPoQuery());
            }
        }
        innerAnnotationSupportWrapper(queryWrapper, query);
        innerConstraintContentsWrapper(queryWrapper, constraintContents);

        return list(queryWrapper);
    }

    /**
     * 统一封装数据范围约束的条件
     * @param queryWrapper
     * @param constraintContents
     */
    default void innerConstraintContentsWrapper(Wrapper<Po> queryWrapper,List<ConstraintCompiledContent> constraintContents){
        if (!isEmpty(constraintContents)) {
            ((AbstractWrapper) queryWrapper).and(wq ->{
                for (ConstraintCompiledContent constraintContent : constraintContents) {
                    ((Nested) wq).or(wqq -> ((Join) wqq).apply(constraintContent.getCompiledSqlContent()));
                }
                return wq;
            });
        }
    }

    /**
     * 额外条件处理
     * @param queryWrapper
     * @param query
     */
    void innerAnnotationSupportWrapper(Wrapper<Po> queryWrapper,BasePoQuery<Po> query);

    /**
     * 根据id删除，带数据约束
     * @param id
     * @param constraintContents
     * @return
     */
    @CacheEvict(value="getById",key = "#id")
    default boolean removeById(String id,List<ConstraintCompiledContent> constraintContents){
        QueryWrapper<Po> queryWrapper = Wrappers.<Po>query().eq(IDBasePo.COLUMN_ID, id);
        innerConstraintContentsWrapper(queryWrapper, constraintContents);

        return remove(queryWrapper);
    }

    /**
     * 根据id查询，带数据约束
     * @param id
     * @param constraintContents
     * @return
     */
    default Po getById(String id,List<ConstraintCompiledContent> constraintContents){
        QueryWrapper<Po> queryWrapper = Wrappers.<Po>query().eq(IDBasePo.COLUMN_ID, id);
        innerConstraintContentsWrapper(queryWrapper, constraintContents);

        return getOne(queryWrapper);
    }

    /**
     * 根据id更新，带数据约束
     * @param po
     * @param constraintContents
     * @return
     */
    @CacheEvict(value="getById",key = "#po.id")
    default boolean updateById(Po po,List<ConstraintCompiledContent> constraintContents){
        return updateById(po, constraintContents, false);
    }

    /**
     * 根据id全量字段更新，带数据约束
     * @param po
     * @param constraintContents
     * @return
     */
    @CacheEvict(value="getById",key = "#po.id")
    default boolean updateFullById(Po po,List<ConstraintCompiledContent> constraintContents){
        return updateById(po, constraintContents, true);
    }

    /**
     * 根据字段更新，带数据约束
     * @param po
     * @param constraintContents
     * @param isFull
     * @return
     */
    @CacheEvict(value="getById",key = "#po.id")
    default boolean updateById(Po po,List<ConstraintCompiledContent> constraintContents,boolean isFull){
        QueryWrapper<Po> queryWrapper = Wrappers.<Po>query().eq(IDBasePo.COLUMN_ID, po.getId());
        innerConstraintContentsWrapper(queryWrapper, constraintContents);
        if(isFull){
            return updateFull(po, queryWrapper);
        }
        return update(po, queryWrapper);
    }
    /**
     * 根据id更新，显示将字段设置为null，不带 数据约束，如果全量字段建议使用updateFullById
     * @param po
     * @return
     */
    @CacheEvict(value="getById",key = "#po.po.id")
    default boolean updateById(BaseUpdateWithNullCondition<Po> po){
        return updateById(po, null);
    }

    /**
     * 根据id更新，显示将字段设置为null，带数据约束，如果全量字段建议使用updateFullById
     * @param po
     * @param constraintContents
     * @return
     */
    @CacheEvict(value="getById",key = "#po.po.id")
    default boolean updateById(BaseUpdateWithNullCondition<Po> po,List<ConstraintCompiledContent> constraintContents){
        UpdateWrapper<Po> update = Wrappers.update();
        Object value = null;
        for (Field field : ReflectUtil.getFieldsDirectly(po.getClass(), false)) {
            value = ReflectUtil.getFieldValue(po, field);
            if (value == null || !(value instanceof Boolean)) {
                continue;
            }
            if(((Boolean) value) == true){
                update.set(StringTool.humpToLine(field.getName()), null);
            }
        }
        update.eq(IDBasePo.COLUMN_ID, po.getPo().getId());
        innerConstraintContentsWrapper(update, constraintContents);
        return update(po.getPo(), update);
    }

    @Cacheable(cacheNames = {"getById"},key = "#id")
    @Override
    Po getById(Serializable id);

    @CacheEvict(value="getById",key = "#entity.id")
    @Override
    boolean updateById(Po entity);

    /**
     * 全字段更新
     * @param entity
     * @param updateWrapper
     * @return
     */
    public boolean updateFull(Po entity, Wrapper<Po> updateWrapper);
    /**
     * 全字段更新
     * @param entity
     * @return
     */
    boolean updateFullById(Po entity);

    @CacheEvict(value="getById",key = "#id")
    @Override
    boolean removeById(Serializable id);

    /**
     * 计数
     * @param query
     * @return
     */
    default int count(Po query){
        return count(Wrappers.query(query));
    }

    default boolean remove(Po query) {
        return remove(Wrappers.query(query));
    }
}
